#include "encodedsqldbaccess.h"

#include <assert.h>

#include <QSqlQuery>
#include <QSqlError>

#include <QFile>
#include <QMessageBox>

namespace
{
    static const QString wugoge62("XcvS6VuTNYVn5UFEtXI9nrrGp6Lk2rGILmjf5d1sffIPwtLCzZSYHHQLqt6oSrq");
}

EncodedSqlDbAccess::EncodedSqlDbAccess(
    QObject *parent)
:   QObject(parent)
,   m_valid(false)
,   m_empty(false)
{
}


EncodedSqlDbAccess::~EncodedSqlDbAccess()
{
    if(m_db.isOpen())
        m_db.close();
}


const bool EncodedSqlDbAccess::open(const QString &dbFilePath)
{
    m_valid = initialize(dbFilePath);
    return isValid();
}


const bool EncodedSqlDbAccess::isValid() const
{
    return m_valid;
}


const bool EncodedSqlDbAccess::isEmpty() const
{
    return m_empty;
}


const bool EncodedSqlDbAccess::isOpen() const
{
    return m_db.isOpen();
}


const bool EncodedSqlDbAccess::initialize(const QString &dbFilePath)
{
    if(m_db.isOpen())
        return false;

#ifdef _DEBUG
    const QString driver("QSQLITE");
#else
    const QString driver("QSQLCIPHER");
#endif

    m_db = QSqlDatabase::addDatabase(driver);
    if(!m_db.isDriverAvailable(driver))
    {
        QMessageBox::critical(0
            , tr("Database driver unavailable.")
            , tr("The database is supposed to be encrypted. The driver '" + driver.toLocal8Bit() + "' was not found.")
            , QMessageBox::Ok);

        return false;
    }

    if(!QFile::exists(dbFilePath) && !createDb(dbFilePath))
        return false;

    return openDb(dbFilePath);
}


const bool EncodedSqlDbAccess::createDb(const QString &dbFilePath)
{
    assert(!m_db.isOpen());

    m_db.setDatabaseName(dbFilePath);
    if(!m_db.open())
    {
        handleSqlError(m_db.lastError());
        return false;
    }

    if(!setupKey(m_db))
        return false;

    m_db.close();

    m_empty = true;
    return true;
}


const bool EncodedSqlDbAccess::openDb(const QString &dbFilePath)
{
    assert(!m_db.isOpen());

    m_db.setDatabaseName(dbFilePath);
    if(!m_db.open())
    {
        handleSqlError(m_db.lastError());
        return false;
    }

    if(!setupKey(m_db))
        return false;

    assert(m_db.isOpen());
    return probeDb();
}


const bool EncodedSqlDbAccess::probeDb() const
{
    QSqlQuery q(m_db);
    if(!exec(q, "select * from sqlite_master limit 1"))
        return false;

    return true;
}


const bool EncodedSqlDbAccess::setupKey(QSqlDatabase &db)
{
    assert(db.isOpen());

#ifndef _DEBUG

    QSqlQuery q(db);
    if(!exec(q, "pragma key = '" + wugoge62.toLocal8Bit() + "';"))
        return false;

#endif

    return true;
}


const bool EncodedSqlDbAccess::exec(
    QSqlQuery &q
,   const QString &query)
{
    if(!q.exec(query))
    {
        handleSqlError(q.lastError());
        return false;
    }
    return true;
}


void EncodedSqlDbAccess::handleSqlError(const QSqlError &error)
{
    static const QStringList ErrorTypeText = QStringList()
        << tr("No error")
        << tr("Connection error")
        << tr("SQL statement syntax error")
        << tr("Transaction failed error")
        << tr("Unknown error");

    if(error.type() == 0)
        return;

    QMessageBox::critical(0
        , ErrorTypeText[error.type()]
        , error.text()
        , QMessageBox::Ok);
}